/*****************************************************************************
 *  This file is part of the OpenDomo project.
 *  Copyright(C) 2011 OpenDomo Services SL
 *  
 *  Daniel Lerch Hostalot <dlerch@opendomo.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *****************************************************************************/



// ----------------------------------------------------------------------------
// author: Daniel Lerch <dlerch@gmail.com>, http://opendomo.com
// date: Noviembre 2008
//
// CHANGES:
//
// ----------------------------------------------------------------------------




#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <sys/ioctl.h>
#include <time.h>

#include <conf.h>


// {{{ getip()
char *getip(const char* iface) 
{
   struct ifreq if_data;
   int sockd;
   u_int8_t  local_mac[6];
   u_int32_t local_ip;
   u_int32_t local_netmask;
   u_int32_t local_broadcast;
   u_int32_t ip;
   struct in_addr in;

   if (getuid () != 0)
   {
      perror ("You must be root. \n");
      return "";
   }

   if ((sockd = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ARP))) < 0)
   {
      perror("socket");
      return "";
   }

   strcpy (if_data.ifr_name, iface);

   if (ioctl (sockd, SIOCGIFADDR, &if_data) < 0) 
   {
      perror ("ioctl(); SIOCGIFADDR \n");
		close(sockd);
      return "";
   }

   memcpy ((void *) &ip, (void *) &if_data.ifr_addr.sa_data + 2, 4);
   local_ip = ntohl (ip);
   in.s_addr = ip;

	close(sockd);
   return inet_ntoa(in);
}
// }}}

// {{{ update_agent
int update_agent(const char* agent, const char *name)
{
   FILE *f = fopen(AGENTFILE, "r+");

   while(f && !feof(f))
   {
      char fagent[64];
      char fname[64];
      char timestamp[64];

      fagent[sizeof(fagent)-1]=0;
      fname[sizeof(fname)-1]=0;
      timestamp[sizeof(timestamp)-1]=0;

      fscanf(f, "%63s\t%63s\t%63s", fagent, fname, timestamp);
		printf("name:%s-\n", fname);
		if(strlen(fagent)==0 || strlen(fname)==0 || strlen(timestamp)==0)
			continue;

      /* update timestamp */
      if(strcmp(fagent, agent)==0)
      {
         printf("update agent %s (%d)\n", agent, strlen(timestamp));
         fseek(f, -strlen(fname)-1-strlen(timestamp), SEEK_CUR);
         fprintf(f, "%s\t%d", name, (int) time(NULL));
         fclose(f);
         return 1;
      }
   }

   if(f)
      fclose(f);

   chown(AGENTFILE, ADMINUID, 0);

   return 0;
}
// }}}

// {{{ add_agent()
void add_agent(const char* agent, const char*name)
{
   FILE *f = fopen(AGENTFILE, "a");

   if(f)
   {
      printf("add agent: %s\n", agent);
      fprintf(f, "%s\t%s\t%d\n", agent, name, (int)time(NULL));
      fclose(f);
   }

   chown(AGENTFILE, ADMINUID, 0);
}
// }}}

// {{{ remove_old_agents()
int remove_old_agents()
{
   FILE *f = fopen(AGENTFILE, "r+");

   while(f && !feof(f))
   {
      char fagent[64];
      char fname[64];
      char timestamp[64];

      fscanf(f, "%63s\t%63s\t%63s", fagent, fname, timestamp);
      fagent[sizeof(fagent)-1]=0;
      fname[sizeof(fname)-1]=0;
      timestamp[sizeof(timestamp)-1]=0;

      if(strcmp(timestamp, TIMESTAMP_AGENT_DOWN)==0)
         continue;

      int df = (int)time(NULL)-atoi(timestamp);
      if(df>AGENT_DOWN_TIME)
      {
         snprintf(timestamp, sizeof(timestamp), "%s", TIMESTAMP_AGENT_DOWN);
         printf("remove agent: %s (df time: %d)\n", fagent, df);
         fseek(f, -strlen(timestamp), SEEK_CUR);
         fprintf(f, "%s", timestamp);
         fclose(f);
         return 1;
      }
   }

   if(f)
      fclose(f);

   chown(AGENTFILE, ADMINUID, 0);

   return 0;
}
// }}}

int main(int argc, char *argv[])
{
   struct sockaddr_in addr;
   int fd, nbytes;
   unsigned int addrlen;
   struct ip_mreq mreq;
   char msgbuf[256];

   if(argc!=2)
   {
      printf("Usage: %s <interface>\n\n", argv[0]);
      exit(0);
   }

   if((fd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 
   {
      perror("socket()");
      exit(1);
   }

   memset(&addr, 0, sizeof(addr));
   addr.sin_family=AF_INET;
   addr.sin_addr.s_addr=htonl(INADDR_ANY); 
   addr.sin_port=htons(PORT);

   if(bind(fd, &addr, sizeof(addr))==-1)
   {
      perror("bind()");
      exit(0);
   }


	while(1) 
	{
		addrlen=sizeof(addr);
		if ((nbytes=recvfrom(fd, msgbuf, sizeof(msgbuf), 0,
						(struct sockaddr *) &addr, &addrlen)) < 0) 
		{
			perror("recvfrom()");
			exit(1);
		}

		printf("pkg: %s\n", msgbuf);

		char t=' ';
		char *ip = strstr(msgbuf, "OPENDOMO AGENT ");
		if(ip)
		{
			t='A';
			ip += strlen("OPENDOMO AGENT "); 
		}
		else
		{
			t='D';
			ip = strstr(msgbuf, "OPENDOMO DEVICE ");
			if(ip)
				ip += strlen("OPENDOMO DEVICE "); 
		}

		char *n = strstr(ip, " ");
		if(n)
		{
			*n=0;
			n++;
		}
		else
			n="UNKNOWN";

		char name[18];
		snprintf(name, sizeof(name), "%c:%s", t, n);
		int i;
		for(i=strlen(name); i<sizeof(name)-1; i++)
			name[i]='_';
		name[i]=0;

		if(!ip)
			continue;

		if(strcmp(ip, getip(argv[1]))==0)
			continue;
		if(!update_agent(ip, name))
			add_agent(ip, name);

		while(remove_old_agents());
	}

   return 0;
}
